package com.xhwl.data.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xhwl.common.pojo.ResultJson;
import com.xhwl.common.pojo.cent.sdata.Enterprise;
import com.xhwl.common.pojo.cent.sdata.Position;
import com.xhwl.common.pojo.cent.sdata.PositionExcelRecord;
import com.xhwl.common.pojo.cent.sdata.personnel.Staff;
import com.xhwl.common.query.cent.sdata.PositionQuery;
import com.xhwl.common.utils.StringUtils;
import com.xhwl.common.vo.cent.sdata.PositionErrorVO;
import com.xhwl.common.vo.cent.sdata.PositionVO;
import com.xhwl.data.dao.IEnterpriseDao;
import com.xhwl.data.dao.IPositionDao;
import com.xhwl.data.service.IPositionExcelRecordService;
import com.xhwl.data.service.IPositionService;
import com.xhwl.data.service.personal.IStaffService;
import com.xhwl.data.util.excel.ExcelExportUtils;
import com.xhwl.data.util.excel.ExcelStyleUtil;
import com.xhwl.data.util.excel.ExcelTypeEnum;
import com.xhwl.data.util.excel.FileNameUtil;
import com.xhwl.starter.obs.IObsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class PositionServiceImpl extends ServiceImpl<IPositionDao, Position> implements IPositionService {

    @Autowired
    private IPositionService positionService;

    @Autowired
    private IPositionDao positionDao;

    @Autowired
    ExcelExportUtils excelExportUtils;

    @Autowired
    IObsService obsService;

    @Autowired
    IPositionExcelRecordService positionExcelRecordService;

    @Autowired
    IEnterpriseDao enterpriseDao;

    @Autowired
    ExcelExportUtils exportUtils;

    @Autowired
    private IStaffService staffService;

    @Override
    public ResultJson nameVerification(List<Position> positions) {
        if (positions.isEmpty()) {
            return ResultJson.fail("参数错误");
        }
        List<String> nameArr = new ArrayList<>(positions.size());
        for (Position position : positions) {
            if (null == position.getName() || position.getName().trim().length() == 0) {
                return ResultJson.fail("参数错误，岗位名称不能为空");
            }
            nameArr.add(position.getName());
        }
        List<String> collect = nameArr.stream().distinct().collect(Collectors.toList());
        if(collect.size() != nameArr.size()){
            return ResultJson.fail(9010, "新增岗位存在名称重复", positions);
        }
        for (Position position : positions) {
            Boolean flag = nameCheck(position);
            if (!flag) {
                return ResultJson.fail(9010, "新增岗位存在名称重复", position);
            }
        }
        return ResultJson.success("名称可用");
    }


    @Override
    public Page page(PositionQuery positionQuery, Page page) {
        LambdaUpdateWrapper<Position> positionLambdaUpdateWrapper = handleRequestParams(positionQuery);
        return positionService.page(page, positionLambdaUpdateWrapper);
    }


    @Override
    public List<Position> list(PositionQuery positionQuery) {
        LambdaUpdateWrapper<Position> positionLambdaUpdateWrapper = handleRequestParams(positionQuery);
        return positionService.list(positionLambdaUpdateWrapper);
    }


    @Override
    public ResultJson batchCreate(List<Position> positions) {
        if (positions.isEmpty()) {
            return ResultJson.fail("数据错误");
        }
        List<Position> newPosition = new ArrayList<>(positions.size());
        for (Position position : positions) {
            Boolean flag = nameCheck(position);
            if (!flag) {
                return ResultJson.fail(9010, "新增岗位存在名称重复", position);
            }
            positionService.save(position);
            newPosition.add(position);
        }
        return ResultJson.success("操作成功", newPosition);
    }


    @Override
    public ResultJson create(Position position) {
        Boolean flag = nameCheck(position);
        if (!flag) {
            return ResultJson.fail("新增岗位存在名称重复");
        }
        this.save(position);
        return ResultJson.success();
    }


    @Override
    public ResultJson update(Position position) {
        Position oldPosition = positionDao.selectById(position.getId());
        if (null == oldPosition) {
            return ResultJson.fail("数据不存在");
        }
        Boolean flag = nameCheck(position);
        if (!flag) {
            return ResultJson.fail("编辑岗位存在名称重复");
        }
        return ResultJson.success(this.updateById(position));
    }


    @Override
    public ResultJson delete(Integer id) {
        Position oldPosition = positionDao.selectById(id);
        if (null == oldPosition) {
            return ResultJson.fail("数据不存在");
        }
        // 关联了员工的不能删除
        QueryWrapper<Staff> staffQueryWrapper = new QueryWrapper<>();
        staffQueryWrapper.select("id");
        staffQueryWrapper.eq("position_id", id);
        staffQueryWrapper.eq("is_deleted", false);
        List<Staff> list = staffService.list(staffQueryWrapper);
        if(!list.isEmpty()){
            return ResultJson.fail("该岗位已经关联员工信息");
        }
        oldPosition.setDeleted(1);
        return ResultJson.success(this.updateById(oldPosition));
    }


    /**
     * 岗位名称校验
     * @param position
     * @return
     */
    public Boolean nameCheck(Position position) {
        QueryWrapper<Position> positionQueryWrapper = new QueryWrapper<>();
        positionQueryWrapper
                .eq("deleted", 0)
                .eq("enterprise_id", position.getEnterpriseId())
                .eq("name", position.getName());
        Position one = positionService.getOne(positionQueryWrapper);
        if (null != one) {
            // 修改本身
            if (null != position.getId() && position.getId().intValue() == one.getId().intValue()) {
                return true;
            }
            return false;
        }
        return true;
    }


    private LambdaUpdateWrapper<Position> handleRequestParams(PositionQuery positionQuery) {
        LambdaUpdateWrapper<Position> queryWrapper = new LambdaUpdateWrapper<>();
        queryWrapper.eq(Position::getDeleted, 0);
        if (null == positionQuery) {
            return queryWrapper;
        }
        queryWrapper
                .orderByDesc(Position::getCreatedAt)
                .eq(null != positionQuery.getEnterpriseId(), Position::getEnterpriseId, positionQuery.getEnterpriseId())
                .like(!StringUtils.isEmpty(positionQuery.getName()), Position::getName, positionQuery.getName());
        return queryWrapper;
    }


    /**
     * 岗位数据导出
     * @param response
     * @param positionQuery
     * @throws IOException
     */
    @Override
    public void positionExport(HttpServletResponse response, PositionQuery positionQuery) throws IOException {
        QueryWrapper<Position> positionQueryWrapper = new QueryWrapper<>();
        positionQueryWrapper
                .eq("deleted", 0)
                .orderByDesc("created_at")
                .in("enterprise_id", positionQuery.getEnterpriseId());
        if (null != positionQuery.getName()) {
            positionQueryWrapper.like("name", positionQuery.getName());
        }
        List<Position> list = positionService.list(positionQueryWrapper);
        String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String fileName = "岗位数据_" + time + ".xlsx";
        // 空文件流
        if (CollectionUtils.isEmpty(list)) {
            ArrayList<PositionVO> emptyList = new ArrayList<>(list.size());
            excelExportUtils.exportExcelStream(emptyList, PositionVO.class, null, "岗位数据",  fileName, response);
        }
        ArrayList<PositionVO> resultList = new ArrayList<>(list.size());
        for (Position position : list) {
            PositionVO positionVO = new PositionVO();
            BeanUtils.copyProperties(position, positionVO);
            resultList.add(positionVO);
        }
        excelExportUtils.exportExcelStream(resultList, PositionVO.class, null, "岗位数据",  fileName, response);
    }


    /**
     * 岗位模板导出
     * @param response
     * @throws IOException
     */
    @Override
    public void exportTemp(HttpServletResponse response) throws IOException {
        String fileName = "岗位管理导入模板";
        String basePath = ResourceUtils.getURL("classpath:").getPath();
        String filePath = basePath + "template/" + fileName + ".xlsx";
        File file = new File(filePath);
        if (file.exists()) {
            response.setHeader("content-type", "application/octet-stream");
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
            response.setHeader("Access-Control-Expose-Headers","Content-Disposition");
            FileInputStream fis = null;
            BufferedInputStream bis = null;
            try {
                fis = new FileInputStream(file);
                Workbook workbook = null;
                try {
                    workbook = new XSSFWorkbook(fis);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                workbook.write(response.getOutputStream());
            } catch (Exception e) {
                log.error(e.getMessage());
            } finally {
                if (bis != null) {
                    try {
                        bis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } else {
            log.error("文件 {} 不存在", filePath);
        }
    }


    /**
     * 岗位数据导入
     * @param file
     * @param enterpriseId
     * @return
     * @throws IOException
     */
    @Override
    public ResultJson readExcel(MultipartFile file, Integer enterpriseId) throws Exception {
        String originalFilename = file.getOriginalFilename();
        String fileNameNoEx = FileNameUtil.getFileNameNoEx(originalFilename);
        String fileNameSuffix = FileNameUtil.getFileNameSuffix(originalFilename);
        if (!(ExcelTypeEnum.XLS.getValue().equals(fileNameSuffix) || ExcelTypeEnum.XLSX.getValue().equals(fileNameSuffix))) {
            return ResultJson.fail("文件格式错误，仅支持导入xls/xlsx文件");
        }
        ImportParams params = new ImportParams();
        params.setHeadRows(1);
        params.setTitleRows(1);
        params.isNeedVerify();
        params.setVerifyFileSplit(false);
        ExcelImportResult<PositionErrorVO> importResult = null;
        try {
            importResult = ExcelImportUtil.importExcelMore(file.getInputStream(), PositionErrorVO.class, params);
        } catch (Exception e) {
            e.printStackTrace();
            return ResultJson.fail("模板解析失败，选择不正确");
        }
        List<PositionErrorVO> importResultList = importResult.getList();
        // 数据存在岗位名称为空的情况，进行处理
        ArrayList<PositionErrorVO> positionList = new ArrayList<>();
        // 全量检查是否重复
        List<String> nameArr = new ArrayList<>(positionList.size());
        for (PositionErrorVO positionErrorVO : importResultList) {
            if (null != positionErrorVO.getName()) {
                nameArr.add(positionErrorVO.getName());
                positionList.add(positionErrorVO);
            }
        }
        List<String> filterArr = nameArr.stream().distinct().collect(Collectors.toList());
        if (filterArr.size() != nameArr.size()) {
            return ResultJson.fail("当前导入岗位名称存在重复");
        }
        if (CollectionUtils.isEmpty(positionList)) {
            return ResultJson.fail("当前无导入数据");
        }
        if (positionList.size() > 500) {
            return ResultJson.fail("当前导入条数超过500条");
        }
        return ResultJson.success(handlExcelData(positionList, enterpriseId, fileNameNoEx, fileNameSuffix));
    }


    /**
     * 数据处理
     * @param positionList
     * @param enterpriseId
     * @param fileName
     * @throws IOException
     */
    public PositionExcelRecord handlExcelData(List<PositionErrorVO> positionList, Integer enterpriseId, String fileName, String fileNameSuffix) throws Exception {
        log.info("===========================正在执行导入===============================");
        int totalSize = positionList.size();
        // 拿到导入前校验失败的list
        List<PositionErrorVO> failPositionList = positionImportVerify(positionList, enterpriseId);
        positionList.removeAll(failPositionList);
        for (PositionErrorVO positionData : positionList) {
            String errorMsg = importPositionExcelData(positionData, enterpriseId);
            if (org.apache.commons.lang3.StringUtils.isNotEmpty(errorMsg)) {
                positionData.setErrorMsg(errorMsg);
                failPositionList.add(positionData);
            }
        }
        int failSize = 0;
        PositionExcelRecord positionExcelRecord = new PositionExcelRecord();
        positionExcelRecord.setFileName(fileName + "." + fileNameSuffix);
        if (!CollectionUtils.isEmpty(failPositionList)) {
            failSize = failPositionList.size();
            log.info("存在导入失败的数据，准备导出到&异步上传华为云");
            String obsUrl = getObsUrl(failPositionList, PositionErrorVO.class, "岗位管理", "岗位管理", fileName, fileNameSuffix);
            int successSize = totalSize - failSize;
            positionExcelRecord.setEnterpriseId(enterpriseId);
            positionExcelRecord.setSuccess(successSize);
            positionExcelRecord.setFail(failSize);
            positionExcelRecord.setResult("成功" + successSize + "条，失败" + failSize + "条，共" + totalSize);
            positionExcelRecord.setFileUrl(obsUrl);
            positionExcelRecordService.save(positionExcelRecord);
        } else {
            positionExcelRecord.setEnterpriseId(enterpriseId);
            positionExcelRecord.setSuccess(totalSize);
            positionExcelRecord.setFail(failSize);
            positionExcelRecord.setResult("全部成功，共" + totalSize);
            positionExcelRecord.setFileUrl(null);
            positionExcelRecordService.save(positionExcelRecord);
        }
        return positionExcelRecord;
    }


    /**
     * 数据校验
     * @param positionList
     * @param enterpriseId
     * @return
     */
    public List<PositionErrorVO> positionImportVerify(List<PositionErrorVO> positionList, Integer enterpriseId) {
        List<PositionErrorVO> failStudentList = new ArrayList<>();
        for (PositionErrorVO positionData : positionList) {
            StringBuffer errorMsg = new StringBuffer();
            Enterprise enterprise = enterpriseDao.selectById(enterpriseId);
            if (null == enterprise) {
                errorMsg.append("不存在该企业信息");
            }
            if (null == positionData.getName().trim()) {
                errorMsg.append("岗位名称不能为空");
            }
            if (null != positionData.getName() && 2 > positionData.getName().trim().length() || positionData.getName().trim().length() > 24) {
                errorMsg.append("岗位名称超过限制长度（2~24）");
            }
            QueryWrapper<Position> positionQueryWrapper = new QueryWrapper<>();
            positionQueryWrapper.eq("deleted", 0)
                    .eq("enterprise_id", enterpriseId)
                    .eq("name", positionData.getName());
            Position one = positionService.getOne(positionQueryWrapper);
            if (null != one) {
                errorMsg.append("新增岗位存在名称重复");
            }
            if (errorMsg.length() > 0) {
                positionData.setErrorMsg(errorMsg.toString());
                failStudentList.add(positionData);
            }
        }
        return failStudentList;
    }


    /**
     * 成功数据写入
     * @param positionData
     * @param enterpriseId
     * @return
     */
    public String importPositionExcelData(PositionErrorVO positionData, Integer enterpriseId) {
        StringBuffer errorMsg = new StringBuffer();
        Position position = new Position();
        position.setName(positionData.getName());
        position.setEnterpriseId(enterpriseId);
        boolean saveResult = save(position);
        if (!saveResult) {
            errorMsg.append("岗位插入数据库失败");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return errorMsg.toString();
        }
        return errorMsg.toString();
    }


    /**
     * 自定义转Excel上传OBS/minio，返回可访问地址
     * @param list
     * @param pojoClass
     * @param title
     * @param sheetName
     * @param fileName
     * @return
     * @throws IOException
     */
    public String getObsUrl(List<?> list, Class<?> pojoClass, String title, String sheetName, String fileName, String fileNameSuffix) throws IOException {
        ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
        exportParams.setStyle(ExcelStyleUtil.class);
        final Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
        Sheet sheet = workbook.getSheetAt(0);
        Row row = sheet.createRow(0);
        row.setHeight((short) 2500);
        Cell cell = row.createCell(0);
        cell.setCellValue("填写须知：\n" +
                "1. 请勿修改表格结构；\n" +
                "2. 标红字段为必填项，黑色字段为选填项；\n" +
                "3. 岗位名称：必填，长度为2-24个字符，支持中文、数字、英文大小写、特殊字符；若填写不符合规范，导入失败；\n");
        CellStyle cellStyle = workbook.createCellStyle();
        Font font = workbook.createFont();
        //字体样式
        font.setFontName("宋体");
        //是否加粗
        font.setBold(false);
        //字体大小
        font.setFontHeightInPoints((short) 10);
        cellStyle.setFont(font);
        cellStyle.setAlignment(HorizontalAlignment.LEFT);
        //设置自动换行
        cellStyle.setWrapText(true);
        cell.setCellStyle(cellStyle);
        String saveFileName = fileName + LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli() + "." + fileNameSuffix;
        //转换出字节流数据，调华为云接口上传
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        workbook.write(bos);
        byte[] byteArray = bos.toByteArray();
        InputStream inputStream = new ByteArrayInputStream(byteArray);
        String obsUrl = obsService.putObject(saveFileName, inputStream, null);
        return obsUrl;
    }

}
